﻿// MyProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//公众号：程序员速成 ，内含一辈子都让你感激自己的优质视频教程，欢迎关注

#include <iostream>
#include <map>

#ifdef _DEBUG   //只在Debug（调试）模式下
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__) //重新定义new运算符
#define new DEBUG_NEW
#endif
#endif

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996) 

namespace _nmsp1
{
	//UI控件类的父类
	class CtlParent
	{
	public:
		CtlParent(string caption) :m_caption(caption) {} //构造函数
		virtual ~CtlParent() {} //做父类时析构函数应该为虚函数

	public:
		//当UI控件发生变化时该成员函数会被调用
		virtual void Changed(map<string, CtlParent*>& tmpuictllist) = 0; //形参所代表的map容器中包含着所有对话框中涉及的UI控件。
		//设置UI控件启用或者禁用
		virtual void Enable(bool sign) = 0;

	protected:
		string m_caption; //控件上面显示的文字内容，本范例假设每个UI空间上的文字都不同
	};

	//普通按钮相关类
	class Button :public CtlParent
	{
	public:
		Button(string caption) :CtlParent(caption) {} //构造函数
		//设置按钮的启用或者禁用
		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "按钮\"" << m_caption << "\"被设置为了\"启用\"状态" << endl;
			}
			else
			{
				cout << "按钮\"" << m_caption << "\"被设置为了\"禁用\"状态" << endl;
			}
			//......具体实现按钮启用或者禁用的代码。。。。
		}
		//按钮被按下时该成员函数会被调用
		virtual void Changed(map<string, CtlParent*>& tmpuictllist);
	};
	
	//单选按钮相关类
	class RadioBtn :public CtlParent
	{
	public:
		RadioBtn(string caption) :CtlParent(caption) {} //构造函数
		//设置单选按钮的启用或者禁用
		virtual void Enable(bool sign)
		{
			//本范例用不到该功能，实现代码略......
		}
		//设置单选按钮被选中或者被取消选中，被选中的单选按钮中间有个黑色的实心圆点
		void Selected(bool sign)
		{
			if (sign == true)
			{
				cout << "单选按钮\"" << m_caption << "\"被选中" << endl;
			}
			else
			{
				cout << "单选按钮\"" << m_caption << "\"被取消选中" << endl;
			}
			//......具体实现单选按钮启用或者取消选中的代码略。。。。。
		}
		
		//单选按钮被单击时该成员函数会被调用
		virtual void Changed(map<string, CtlParent*>& tmpuictllist);
	};

	//编辑框相关类
	class EditCtl :public CtlParent
	{
	public:
		EditCtl(string caption) :CtlParent(caption) {} //构造函数
		//设置编辑框的启用或者禁用
		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "编辑框\"" << m_caption << "\"被设置为了\"启用\"状态" << endl;
			}
			else
			{
				cout << "编辑框\"" << m_caption << "\"被设置为了\"禁用\"状态" << endl;
			}
			//......具体实现编辑框启用或者禁用的代码。。。。
		}

		//是否编辑框中的内容为空
		bool isContentEmpty()
		{
			return m_content.empty();
		}

		//编辑框内容发生变化时该成员函数会被调用
		virtual void Changed(map<string, CtlParent*>& tmpuictllist);

	private:
		string m_content = ""; //编辑框中的内容
	};

	//---------------------------
	//普通按钮被按下是该成员函数会被调用
	void Button::Changed(map<string, CtlParent*>& tmpuictllist)
	{
		if (m_caption == "登录")
		{
			//按下的是登录按钮
			cout << "开始游戏登录验证，根据验证结果决定是否进入游戏之中还是验证失败给出提示！" << endl;
		}
		else if (m_caption == "退出")
		{
			//按下的是退出按钮，则退出整个游戏
			cout << "游戏退出，再见！" << endl;
		}
	}

	//单选按钮被单击时该成员函数会被调用
	void RadioBtn::Changed(map<string, CtlParent*>& tmpuictllist)
	{
		if (m_caption == "游客登录")
		{
			(static_cast<RadioBtn*>(tmpuictllist["游客登录"]))->Selected(true); //游客登录 单选按钮被选中
			(static_cast<RadioBtn*>(tmpuictllist["账号登录"]))->Selected(false); //账号登录 单选按钮被取消选中

			tmpuictllist["账号"]->Enable(false); //账号 编辑框设置为禁用
			tmpuictllist["密码"]->Enable(false); //密码 编辑框设置为禁用

			tmpuictllist["登录"]->Enable(true);  //登录 按钮设置为启用
		}
		else if (m_caption == "账号登录")
		{
			(static_cast<RadioBtn*>(tmpuictllist["账号登录"]))->Selected(true); //账号登录 单选按钮被选中
			(static_cast<RadioBtn*>(tmpuictllist["游客登录"]))->Selected(false); //游客登录 单选按钮被取消选中

			tmpuictllist["账号"]->Enable(true); //账号 编辑框设置为启用
			tmpuictllist["密码"]->Enable(true); //密码 编辑框设置为启用

			if ((static_cast<EditCtl*>(tmpuictllist["账号"]))->isContentEmpty()
				|| (static_cast<EditCtl*>(tmpuictllist["密码"]))->isContentEmpty())
			{
				//如果 账号 编辑框 或者 密码 编辑框 有一个为空，则不允许登录
				tmpuictllist["登录"]->Enable(false); //登录 按钮设置为禁用
			}
			else
			{
				tmpuictllist["登录"]->Enable(true); //登录 按钮设置为启用
			}			
		}
	}

	//编辑框内容发生变化时该成员函数会被调用
	void EditCtl::Changed(map<string, CtlParent*>& tmpuictllist)
	{
		if ((static_cast<EditCtl*>(tmpuictllist["账号"]))->isContentEmpty()
			|| (static_cast<EditCtl*>(tmpuictllist["密码"]))->isContentEmpty())
		{
			//如果 账号 编辑框 或者 密码 编辑框 有一个为空，则不允许登录
			tmpuictllist["登录"]->Enable(false); //登录 按钮设置为禁用
		}
		else
		{
			tmpuictllist["登录"]->Enable(true); //登录 按钮设置为启用
		}
	}
}
namespace _nmsp2
{
	//类的前向声明
	class CtlParent;

	//中介者父类
	class Mediator
	{
	public:
		virtual ~Mediator() {} //做父类时析构函数应该为虚函数
	public:
		virtual void createCtrl() = 0; //创建所有需要用到的UI控件
		virtual void ctlChanged(CtlParent*) = 0; //当某个UI控件发生变化时调用中介者对象的该成员函数来通知中介者
	};

	//UI控件类的父类
	class CtlParent
	{
	public:
		CtlParent(Mediator* ptmpm, string caption) :m_pmediator(ptmpm), m_caption(caption) {} //构造函数
		virtual ~CtlParent() {} //做父类时析构函数应该为虚函数

	public:
		//当UI控件发生变化时该成员函数会被调用
		virtual void Changed()
		{
			m_pmediator->ctlChanged(this); //通知中介者对象，所有事情让中介者对象去做
		}

		//设置UI控件的启用或禁用
		virtual void Enable(bool sign) = 0;

	protected:
		Mediator* m_pmediator; //指向中介者对象的指针
		string m_caption; //控件上面显示的文字内容，可能并不是所有控件都需要但这里为显示方便依旧引入
	};

	//普通按钮相关类
	class Button :public CtlParent
	{
	public:
		Button(Mediator* ptmpm, string caption) :CtlParent(ptmpm, caption) {} //构造函数
		//设置按钮的启用或者禁用
		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "按钮\"" << m_caption << "\"被设置为了\"启用\"状态" << endl;
			}
			else
			{
				cout << "按钮\"" << m_caption << "\"被设置为了\"禁用\"状态" << endl;
			}
			//具体实现按钮启用或者禁用的代码略......
		}
	};

	//单选按钮相关类
	class RadioBtn :public CtlParent
	{
	public:
		RadioBtn(Mediator* ptmpm, string caption) :CtlParent(ptmpm, caption) {} //构造函数
		//设置按钮的启用或者禁用
		virtual void Enable(bool sign)
		{
			//用不到该功能，实现代码略......
		}
		//设置单选按钮为被选中或者被取消选中，被选中的单选按钮中间有个黑色实心圆点
		void Selected(bool sign)
		{
			if (sign == true)
			{
				cout << "单选按钮\"" << m_caption << "\"被选中" << endl;
			}
			else
			{
				cout << "单选按钮\"" << m_caption << "\"被取消选中" << endl;
			}
			//具体实现单选按钮被选中或者被取消选中的代码略......
		}
	};

	//编辑框相关类
	class EditCtl :public CtlParent
	{
	public:
		EditCtl(Mediator* ptmpm, string caption) :CtlParent(ptmpm, caption) {} //构造函数
		//设置编辑框的启用或者禁用
		virtual void Enable(bool sign)
		{
			if (sign == true)
			{
				cout << "编辑框\"" << m_caption << "\"被设置为了\"启用\"状态" << endl;
			}
			else
			{
				cout << "编辑框\"" << m_caption << "\"被设置为了\"禁用\"状态" << endl;
			}
			//具体实现编辑框启用或者禁用的代码略......
		}
		//是否编辑框中的内容为空
		bool isContentEmpty()
		{
			return m_content.empty();
		}
		//其他成员函数略......
	private:
		string m_content = ""; //编辑框中的内容
	};

	//----------------
	//具体中介者类
	class concreMediator : public Mediator
	{

	public:
		~concreMediator() //析构函数
		{
			if (mp_login) {delete mp_login; mp_login = nullptr;}
			if (mp_logout) { delete mp_logout; mp_logout = nullptr; }

			if (mp_rbtn1) { delete mp_rbtn1; mp_rbtn1 = nullptr; }
			if (mp_rbtn2) { delete mp_rbtn2; mp_rbtn2 = nullptr; }

			if (mp_edtctl1) { delete mp_edtctl1; mp_edtctl1 = nullptr; }
			if (mp_edtctl2) { delete mp_edtctl2; mp_edtctl2 = nullptr; }
		}
		virtual void createCtrl()
		{
			//当然，各种UI控件对象在外面创建，然后把地址传递进来也可以
			mp_login = new Button(this, "登录");
			mp_logout = new Button(this, "退出");

			mp_rbtn1 = new RadioBtn(this, "游客登录");
			mp_rbtn2 = new RadioBtn(this, "账号登录");

			mp_edtctl1 = new EditCtl(this, "账号编辑框");
			mp_edtctl2 = new EditCtl(this, "密码编辑框");

			//设置一下缺省的UI控件状态
			mp_rbtn1->Selected(true); //"游客登录"单选按钮设置为选中
			mp_rbtn2->Selected(false); //"账号登录"单选按钮设置为取消选中

			mp_edtctl1->Enable(false); //"账号"编辑框设置为禁用
			mp_edtctl2->Enable(false); //"密码"编辑框设置为禁用

			mp_login->Enable(true); //"登录"按钮设置为启用
			mp_logout->Enable(true); //"退出"按钮设置为启用

			//UI控件的位置设置等代码略......
		}

		virtual void ctlChanged(CtlParent* p_ctrl)
		{
			if (p_ctrl == mp_login) //登录按钮被单击
			{
				cout << "开始游戏登录验证，根据验证结果决定是进入游戏之中还是验证失败给出提示！" << endl;
			}
			else if (p_ctrl == mp_logout) //退出按钮被单击
			{
				cout << "游戏退出，再见!" << endl;
			}

			if (p_ctrl == mp_rbtn1) //游客登录单选按钮被单击
			{
				mp_rbtn1->Selected(true); //"游客登录"单选按钮设置为选中 
				mp_rbtn2->Selected(false); //"账号登录"单选按钮设置为取消选中 

				mp_edtctl1->Enable(false); //"账号"编辑框设置为禁用
				mp_edtctl2->Enable(false); //"密码"编辑框设置为禁用

				mp_login->Enable(true); //"登录"按钮设置为启用
			}
			else if (p_ctrl == mp_rbtn2) //账号登录单选按钮被单击
			{
				mp_rbtn1->Selected(false); //"游客登录"单选按钮设置为取消选中 
				mp_rbtn2->Selected(true); //"账号登录"单选按钮设置为选中 

				mp_edtctl1->Enable(true); //"账号"编辑框设置为启用
				mp_edtctl2->Enable(true); //"密码"编辑框设置为启用

				if (mp_edtctl1->isContentEmpty() || mp_edtctl2->isContentEmpty())
				{
					//如果"账号"编辑框或者"密码"编辑框有一个为空，则不允许登录
					mp_login->Enable(false); //"登录"按钮设置为禁用
				}
				else
				{
					mp_login->Enable(true); //"登录"按钮设置为启用
				}
			}

			if (p_ctrl == mp_edtctl1 || p_ctrl == mp_edtctl2) //账号或者密码编辑框内容发生改变
			{
				if (mp_edtctl1->isContentEmpty() || mp_edtctl2->isContentEmpty())
				{
					//如果"账号"编辑框或者"密码"编辑框有一个为空，则不允许登录
					mp_login->Enable(false); //"登录"按钮设置为禁用
				}
				else
				{
					mp_login->Enable(true); //"登录"按钮设置为启用
				}
			}
		}

	public: //为了方便外界使用，这里以public修饰，实际项目中可以写一个成员函数来return这些指针
		Button* mp_login = nullptr; //登录按钮
		Button* mp_logout = nullptr; //退出按钮

		RadioBtn* mp_rbtn1 = nullptr; //游客登录单选按钮
		RadioBtn* mp_rbtn2 = nullptr; //账号登录单选按钮

		EditCtl* mp_edtctl1 = nullptr; //账号编辑框
		EditCtl* mp_edtctl2 = nullptr; //密码编辑框
	};
}

int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口

	//第17章 中介者（Mediator）模式：调停者模式，行为型模式。
	//（1）中介者的基本概念
	//a)计算的各个组成部件：主板
	//b)qq聊天：与好友或者陌生人单独聊天（私聊）；加入到一个qq群中。
	//c)飞机的安全飞行与安全着陆：塔台

	//（2）中介者模式范例的引入:事件类驱动的软件中有比较广泛的应用，尤其是常常运用在程序的UI界面设计中
	//（3）引入中介者（Mediator）模式
	//定义：用一个中介对象（中介者）来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用，从而使其耦合松散，
	  //而且可以独立地改变他们之间的交互。
	//4种角色
	//a)Mediator(抽象中介者类），Mediator类。
	//b)ConcreteMediator(具体中介者类）：concrMediator类。
	//c)Colleague(抽象同事类)，CtlParent类。
	//d)ConcreteColleague(具体同事类），Button,RadioBtn,EditCtrl类。
	//中介者模式一般用于一组对象以定义良好但复杂的方式进行通信的场合，由中介者负责控制和协调一组对象之间的交互。
	//优缺点：
	//a)集中处理复杂的对象之间的沟通和控制，将以往多对多简化为1对多。
	//b)将同事对象进行解耦。
	//c)将各种控制逻辑代码都集中（转移）到了中介者类中实现。可能会中介者类的实现变得非常复杂甚至难以维护。要平衡好交互复杂度和实现复杂度。
	//如果中介者实现的太复杂，可能会抵消掉使用该模式在其他方面带来的好处。

	//在什么情况下考虑使用中介者模式：
	//a)对象之间引用关系过于复杂，某个对象变化会导致大量其他对象的变化。
	//b)某个对象与大量其他对象进行连接，通信从而造成该对象很难被复用。
	//c)希望将多个类（同事类）中的行为封装到一个类（中介者类）中来实现。




	/*
	//创建各种UI控件
	map<string, _nmsp1::CtlParent*> uictllist; //将所有创建的UI控件保存到map容器中，方便进行参数传递
	uictllist["登录"] = new _nmsp1::Button("登录");
	uictllist["退出"] = new _nmsp1::Button("退出");

	uictllist["游客登录"] = new _nmsp1::RadioBtn("游客登录");
	uictllist["账号登录"] = new _nmsp1::RadioBtn("账号登录");

	uictllist["账号"] = new _nmsp1::EditCtl("账号");
	uictllist["密码"] = new _nmsp1::EditCtl("密码");

	//设置一下缺省的UI控件状态
	(static_cast<_nmsp1::RadioBtn*>(uictllist["游客登录"]))->Selected(true); //游客登录 单选按钮被选中
	(static_cast<_nmsp1::RadioBtn*>(uictllist["账号登录"]))->Selected(false); //账号登录 单选按钮被取消选中

	uictllist["账号"]->Enable(false); //账号  编辑框设置为禁用
	uictllist["密码"]->Enable(false); //密码  编辑框设置为禁用

	uictllist["登录"]->Enable(true); //登录  按钮设置为启用
	uictllist["退出"]->Enable(true); //退出  按钮设置为启用

	cout << "------------------------" << endl;
	uictllist["账号登录"]->Changed(uictllist); //模拟 账号登录 单选按钮被单击选中



	//释放资源
	for (auto iter = uictllist.begin(); iter != uictllist.end(); ++iter)
	{
		delete iter->second;
		iter->second = nullptr;
	}
	*/

	_nmsp2::concreMediator mymedia;
	mymedia.createCtrl();
	cout << "-------当\"账号登录\"单选按钮被按下时:---------" << endl;
	mymedia.mp_rbtn2->Changed(); //模拟"账号登录"单选按钮被按下，则去通知中介者，由中介者实现具体的逻辑处理

	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件

